package com.allo2o.common.biz.sequence.longimpl;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import com.allo2o.common.biz.sequence.Sequence;
import com.allo2o.common.biz.sequence.exception.SequenceException;

public class LongSequence implements Sequence<Long> {

    /**
     * 
     */
    private static final long          serialVersionUID = 998106416015284516L;

    private final Lock                 lock;

    private String                     name;

    private LongSequenceRangeGenerator sequenceRangeGenerator;

    private volatile LongSequenceRange sequenceRange;

    public LongSequence() {
        lock = new ReentrantLock();
    }

    @Override
    public Long nextValue() throws SequenceException {
        if (sequenceRange == null) {
            lock.lock();
            try {
                if (sequenceRange == null) {
                    sequenceRange = sequenceRangeGenerator.nextRange(name);
                }
            } finally {
                lock.unlock();
            }
        }
        Long value = sequenceRange.getAndIncrement();
        if (value.longValue() == -1L) {
            lock.lock();
            try {
                while (true) {
                    if (sequenceRange.isOver()) {
                        sequenceRange = sequenceRangeGenerator.nextRange(name);
                    }
                    value = sequenceRange.getAndIncrement();
                    if (value.longValue() != -1L) {
                        break;
                    }
                }
            } finally {
                lock.unlock();
            }
        }
        if (value.longValue() < 0L) {
            throw new SequenceException("Sequence value overflow, value = " + value);
        }
        return value;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public LongSequenceRangeGenerator getSequenceRangeGenerator() {
        return sequenceRangeGenerator;
    }

    public void setSequenceRangeGenerator(LongSequenceRangeGenerator sequenceRangeGenerator) {
        this.sequenceRangeGenerator = sequenceRangeGenerator;
    }

    @Override
    public String toString() {
        return "LongSequence [name=" + name + "]";
    }

}
